package com.yc.thread.interrupte;

import java.util.concurrent.locks.LockSupport;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.TimeUnit;

/**
 * 在这个示例中，我们创建了两个线程，都试图获取同一个ReentrantLock实例。
 * 我们使用`lockInterruptibly()`方法获取锁，这样当一个线程等待锁时，可以响应中断。
 *
 * `main()`方法中，我们启动两个线程，让它们尝试获取锁。
 * 然后，我们等待1秒，以确保两个线程都开始运行。接着，我们中断第二个线程，使用`thread2.interrupt()`方法。
 * 当第二个线程被中断时，`lockInterruptibly()`方法抛出InterruptedException异常，
 * 我们在异常处理代码中输出线程被中断的信息，并重新设置线程的中断状态。
 *
 * 最后，我们等待两个线程执行完毕，示例结束。
 */
public class ReentrantLockInterruptExample {
    private final ReentrantLock lock = new ReentrantLock();

    public static void main(String[] args) throws InterruptedException {
        ReentrantLockInterruptExample example = new ReentrantLockInterruptExample();

        // 创建并启动两个线程
        Thread thread1 = new Thread(() -> example.method2(), "Thread 1");
        Thread thread2 = new Thread(() -> example.method2(), "Thread 2");
        thread1.start();
        TimeUnit.MILLISECONDS.sleep(1);//保证线程1先抢到锁，然后把线程2堵住
        thread2.start();

        // 等待两个线程都开始运行
        TimeUnit.SECONDS.sleep(10);

        // 中断第二个线程
        thread2.interrupt();

        // 等待两个线程执行完毕
        thread1.join();
        thread2.join();
    }

    /**
     * 证明了：lockInterruptibly()可以在等待的时候被打断
     */
    public void method() {
        try {
            System.out.println(Thread.currentThread().getName() + " trying to acquire the lock...");
            lock.lockInterruptibly(); // 可以响应中断的锁获取方法（替换原来动lock.lock()方法）

            try {
                System.out.println(Thread.currentThread().getName() + " acquired the lock.");
                // 临界区代码
                TimeUnit.SECONDS.sleep(5);
                System.out.println(Thread.currentThread().getName() + "--------------2-");
            } finally {
                lock.unlock();
                System.out.println(Thread.currentThread().getName() + " released the lock.");
            }
        } catch (InterruptedException e) {
            // 处理中断逻辑
            System.out.println(Thread.currentThread().getName() + " was interrupted.");
            Thread.currentThread().interrupt(); // 重新设置中断状态
        }
    }

    /**
     * 证明了：lock()在等待的时候不会被打断
     */
    public void method2() {
        try {
            System.out.println(Thread.currentThread().getName() + " trying to acquire the lock...");
            lock.lock(); // 可以响应中断的锁获取方法

            try {
                System.out.println(Thread.currentThread().getName() + " acquired the lock.");
                // 临界区代码
                TimeUnit.SECONDS.sleep(5);
                System.out.println(Thread.currentThread().getName() + "--------------2-");
            } finally {
                lock.unlock();
                System.out.println(Thread.currentThread().getName() + " released the lock.");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
            // 处理中断逻辑
            System.out.println(Thread.currentThread().getName() + " was interrupted.");
            Thread.currentThread().interrupt(); // 重新设置中断状态
        }
    }

    /**
     * 证明：synchronized不会响应中断
     * 这个实验根本没必要做。因为如果排除了干扰项（sleep），单独使用synchronized根本没有检查异常，也就是根本无法catch InterruptedException
     * 所以代码写到这里，就已经证明了：synchronized不会响应中断
     */
    public void method3() {
//        try {
            System.out.println(Thread.currentThread().getName() + " trying to acquire the lock...");
            synchronized (this) {
                System.out.println(Thread.currentThread().getName() + " acquired the lock.");
                // 临界区代码：得找一个特别耗时间的操作，而不能用sleep或者wait，因为这两个操作是会响应中断的
                // 这样就引入了实验干扰因素
                timeConsumingOperation();
//                TimeUnit.SECONDS.sleep(5);
                System.out.println(Thread.currentThread().getName() + "--------------2-");
            }
            System.out.println(Thread.currentThread().getName() + " released the lock.");

//        } catch (InterruptedException e) {
//            e.printStackTrace();
//            // 处理中断逻辑
//            System.out.println(Thread.currentThread().getName() + " was interrupted.");
//            Thread.currentThread().interrupt(); // 重新设置中断状态
//        }
    }

    /**
     * 这段代码相当于sleep（5000）的意思，但它又不是sleep
     * 之所以写这段代码，是因为sleep本身是会响应中断，从而终止休眠，并报错。而我要证明在运行中（临界区）是否会响应中断，
     * 就不能用sleep（5000）来模拟运行，因为这相当于引入了实验干扰项。
     */
    public void timeConsumingOperation() {
        long targetDuration = TimeUnit.SECONDS.toNanos(5); // 5 seconds in nanoseconds
        long start = System.nanoTime();
        while (true) {
            long now = System.nanoTime();
            long elapsed = now - start;
            if (elapsed >= targetDuration) {
                break;
            }
            // 使用LockSupport.parkNanos()让线程短暂休眠
            LockSupport.parkNanos(1_000_000); // 1 millisecond
        }
    }

    /**
     * lockInterruptibly()可以在等待的时候被打断。那么在运行时会被打断吗？
     * 答：不会。看了AQS源码，我们知道：锁根本不关心程序动运行过程。它只会在线程碰到lock.lock()， conditon.await()等节点时起作用。
     * 线程运行过程中和锁没半毛钱关系，锁并不会去监视线程的运行。就像高速公路动收费站，只会在开始结尾时管你，中间并不会管你干什么。
     * 而"等待中"之所以可以被中断，那是因为"等待中"并不是属于运行时。相当于你还堵在收费站门口。所以它才可以中断你。
     * 那么synchronized也是一样，只不过它在等待过程中也不存在中断操作。
     *
     * 想想：synchronized是怎么释放锁的
     * 答：synchronized无论是正常结束，还是异常结束，都是有JVM自动释放锁动。
     * 看反编译的带synchronized的java代码，出synchronized时，有两个出口(monitorexit)：一个正常出口，一个异常出口。
     *
     */
}

